package dao;

import fs.DBConfig;
import fs.Data;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 *
 * @author lucifer
 */
// ATTENTION: Esta clase **no** debe ser pública. Sólo tiene que poder usarse
// dentro del paquete dao—i.e. debe declararse como `class DB {...}' y **no**
// como `public class DB {...}'.
class DB { // No agregarle `public'!!!

  // La IP se puede settear desde afuera.
  public static String IP = "localhost";

  // El port también se puede settear desde afuera.
  public static String PORT = "5432";

  // El username lo elige el usuario desde afuera.
  public static String USR = "postgres";

  // El password lo elige el usuario desde afuera.
  public static String PASSWD = "1234";

  // El nombre de la DB también se elige desde afuera
  public static String DBNAME = "senzu";

  private final String drv = "jdbc:postgresql://";
  private String url = drv + IP + ":" + PORT + "/" + DBNAME;

  private final Connection conn;
  private static DB db = null;

  public static DB PQ() throws SQLException {
    if (db == null) {
      initConfig();
      db = new DB();
    }
    return db;
  }

  private static void initConfig() {
    try {
      DBConfig config = (DBConfig) Data.ReadFrom(Data.DB_SETTINGS);
      IP = config.IP;
      PORT = config.PORT;
      USR = config.USR;
      PASSWD = config.PASSWD;
      DBNAME = config.DBNAME;
    }
    catch (Exception e) {
      System.out.printf("?loading db default settings\n");
    }
  }

  public Connection Conn() {
    return conn;
  }

  private DB() throws SQLException {
    try {
      Class.forName("org.postgresql.Driver");
    } catch (ClassNotFoundException e) {
      System.out.printf("?postgresql driver\n");
    }

    /* ATTENTION: Debugging code
    conn = DriverManager.getConnection(url, USR, PASSWD);

    /*/ // Good code
    boolean dbCreated = initDB();
    conn = DriverManager.getConnection(url, USR, PASSWD);

    if (dbCreated) {
      initTables();
    }
    //*/

  }

  private boolean initDB() throws SQLException {
    boolean flag = false;

    Connection c = DriverManager.getConnection(drv + IP + ":" + PORT + "/", USR, PASSWD);
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery(
      "select 1 from pg_database where datname = '" + DBNAME + "'"
    );

    /* ATTENTION: Debugging code
    s.execute("drop database if exists " + dbname);
    s.execute("create database " + dbname);
    flag = true;/* ATTENTION: Debugging code
    s.execute("drop database if exists " + dbname);
    s.execute("create database " + dbname);
    flag = true;
    /*/ // FINAL VERSION: good code
    if (!r.next()) {
      s.execute("create database " + DBNAME);
      flag = true;
    }
    //*/

    r.close();
    s.close();
    c.close();

    return flag;
  }

  private void initTables() throws SQLException {
    Statement s = conn.createStatement();

    /* ATTENTION: Debugging code
    s.execute("drop schema if exists public cascade;");
    s.execute("create schema public;");
    //*/

    s.execute(ObraSocialDAO.Tab);
    s.execute(ObraSocialPlanDAO.Tab);
    s.execute(CoberturaDAO.Tab);
    s.execute(PacienteDAO.Tab);

    s.execute(EspecialidadDAO.Tab);
    s.execute(MedicoDAO.Tab);

    s.execute(HistoriaClinicaDAO.Tab);
    s.execute(HorarioDAO.Tab);
    s.execute(TarifaDAO.Tab);
    s.execute(TurnoDAO.Tab);
    s.execute(UserDAO.Tab);

    //*/ Estos son los datos de prueba para tener una version funcional de la
    // aplicación, en la version final se podrían dejar ó al menos se podrían
    // entregar dos App.jar—uno sin datos hardcodeados, y otro con estos datos
    // de prueba para que el docente tenga algo para probar fácilmente.
    s.execute(ObraSocialDAO.Tuples);
    s.execute(ObraSocialPlanDAO.Tuples);
    s.execute(CoberturaDAO.Tuples);
    s.execute(PacienteDAO.Tuples);

    s.execute(EspecialidadDAO.Tuples);
    s.execute(MedicoDAO.Tuples);

    s.execute(HistoriaClinicaDAO.Tuples);
    s.execute(HorarioDAO.Tuples);
    s.execute(TarifaDAO.Tuples);
    s.execute(UserDAO.Tuples);
    //*/

    s.close();
  }

  // Devuelve el valor del último id de una tabla.
  public int LastIDFrom(String table) throws SQLException  {
    Statement s = conn.createStatement();
    ResultSet r = s.executeQuery("select max(id) from " + table);
    if (!r.next()) {
      throw new SQLException("?table is empty");
    }
    int id = r.getInt(1);
    r.close();
    s.close();
    return id;
  }

}
